<?php

class Controller extends fluxView {
    
	protected $Action              = NULL;
	protected $Controller          = NULL;
	private   $ControllersPath     = NULL;
	private   $Parameters          = array ();
	private   $Instance            = NULL;
	private   $superGlobals        = 'ON';
	protected $defController       = NULL;
	protected $defAction           = NULL;
	private   $strict              = FALSE;
	protected $override            = NULL;
	private   $errorHandlerAction  = 'errorHandler';
	protected $hasErrorHandler     = FALSE;
	private   $controllerSuffix    = 'Controller';
	private   $controllerExtension = '.flx';
	private   $actionSuffix        = 'Action';
	private   $useView             = NULL;
    
	public function doMagick () {  //Documented and exemplified

	  //Default paths

		fluxApache::createRedirectHtaccess ();

		$this -> setControllersPath ();
		$this -> setViewsPath ();
		$this -> setControllerAction();
		$this -> createStructure ($this->getDefaultStructure());
		$this -> loadController ();
		$this -> setControllerInstance();
		$this -> loadView($this->useView); 

	}

	public function setSuperGlobals ($bool=NULL) {

		if (strtoupper($bool)!='OFF'||strtoupper($bool)!=='ON')
			throw (new fluxXception ('setSuperGlobals takes one of ->ON or OFF<-'));


		$this->superGlobals=$bool;
	
	}

	public function getSuperGlobals () {

		return $this->superGlobals;

	}

	public function getControllersPath () {
	
		return ($this -> ControllersPath);

	}

	private function hasStartingMethod () {

			  if (method_exists ($this->Instance,'start'))
						 return TRUE;

			  return FALSE;

	}

	public function setDefaultController ($controller=NULL) {

			if (is_null ($controller) || empty ($controller))
				throw (new fluxXception ('Default Controller name should be not empty or null!'));

			return $this -> defController = $controller;

	}
	
	public function setDefaultAction ($action=NULL) {

			if (is_null ($action) || empty ($action))
				throw (new fluxXception ('Default Action should be not empty or null!'));

			//Check if the action exists as well ? no ....

			return $this -> defAction = $action;

	}

	public function setControllerSuffix ($suffix=NULL) {
			  $this->controllerSuffix = $suffix;
	}

	public function setControllerExtension ($extension=NULL) {

			  if(preg_match ('#^\.#',$extension)) {
						 $this->controllerExtension = $extension;
						 return;
			  }

			  $this->controllerExtension = '.'.$extension;

	}

	public function setActionSuffix ($actionSuffix=NULL) {

			  $this->actionSuffix = $actionSuffix;

	}

	private function loadController () {

			//If the controller doesnt exists and we have a default controller set
			//then load the default controller instead of whatever gibberish the user might 
			//have entered.
  
			if (!$this->isValidController()&&!is_null($this->defController)) {

					$cont = $this->ControllersPath._DS.$this->defController
							  . $this->controllerSuffix.$this->controllerExtension;

					$this->Controller = $this->defController;

			} else {

					  $cont = $this->ControllersPath._DS.$this->Controller . 
								 $this->controllerSuffix.$this->controllerExtension;

			}

			//Here we use a plain require once hence, fluxFile::load($file) would use file_exists
			//yet again (because isValidController uses that aswell) so we save a bit of processing
			//time by using require_once.

			fluxLoader::Load ($cont);

	}

	//We cannot use exceptions in isValidController hence we are counting
	//on going on with the work if theres a default controller set

	private function isValidController ($controller=NULL) {

		if (!is_null ($controller))
			return file_exists($this->ControllersPath . _DS . $controller . $this->controllerSuffix.$this->controllerExtension);

			return file_exists($this->ControllersPath . _DS . $this->Controller.$this->controllerSuffix.$this->controllerExtension);

	}


	/* setControllerErrorHandlerAction:
	 *
	 *	Works in the following way; If there is no action found for the given controller
	 * your errorHandler comes in effect. of course with this function you can set
	 * the name of the action that will handle "No action found for the given controller"
	 * i.e: $this->setControllerErrorHandlerAction = 'myErrorHandlerAction';
	 * You would then define into the given controller (for instance indexController.flx)
	 * a method as this one: function myErrorHandlerAction () { echo 'Whoops not found!'; }
	 *
	 */ 

	public function setControllerErrorHandlerAction ($name=NULL){
			  if (is_null ($name)||empty($name))
						 throw (new fluxXception ('Given error handler action name is empty or null!'));

			  $this->errorHandlerAction = $name;

			  return;

	}

	public function getControllerErrorHandlerAction ($name=NULL){
			  return $this->errorHandlerAction;
	}


	private function controllerHasErrorAction () {

			  if (method_exists ($this->Instance,$err=$this->getControllerErrorHandlerAction()))
						 return $err;

			  return FALSE;

	}

	private function actionExists () {
	  
		$action       = $this->Action . $this->actionSuffix;
		$override     = $this->Action . 'Override';

		if (method_exists ($this->Instance, $override)) {
				  $this->override=TRUE;
				  return TRUE;
		}

		//The action doesnt exists into the controller

		if (!method_exists ($this -> Instance, $action)) { 

				  //So we check if the controller has an error action handler
				  //And we also have to check if theres a default action to be executed

				  //First we check if theres a default action to be executed

				  if (method_exists ($this->Instance,$this->defAction.$this->actionSuffix)) {
							 $this->Action=$this->defAction;
							 return $this->defAction.$this->actionSuffix;
				  }

				  //If it doesnt has a default action to be executed we check if it has an error
				  //handler 

				  if ($eAction=$this->controllerHasErrorAction()) {
							 $this->hasErrorHandler = TRUE;
							 return TRUE; //Yes, the action exists .. its AN ERROR because it doesnt exists!
				  }


				  //Or else, we throw a nice exception

					$msg = 'The action ' . $action . ' doesnt exists into the given controller ->'    .
					$this -> Controller . 'Controller <-' . ' and i couldnt find any error handler'   . 
					'named ' . $eAction . ' to be called';

			throw (new fluxXception ($msg,0,$this->Controller,$this->Action));

		}

	}

	private function getDefaultStructure () {

		$structure = array (
			'application' . _DS . 'controllers',
			'application' . _DS . 'views',
			'application' . _DS . 'models'
		);

		return ($structure);

	}

	private function createStructure ($structure) {

	  foreach ($structure as $s) {
			
			if (is_writeable ($s)||is_dir($s))
				continue;

			mkdir ($s,0777,TRUE);

		}

		return;

	}

	//Depends on fluxMisc::checkSyntaxErrors

	private function setControllerInstance () {

		if (!isset ($this->Controller) || is_null ($this->Controller)) {
			$message = 'Unable to load controller ' . $this -> Controller;
			throw (new fluxXception ($message));
		}

		//actually we would call fluxLoader for it to
		//load the controller according to the given structure
		//Instead of using a plain require_once, for checking class names, etc

		$controller = $this -> Controller . $this->controllerSuffix;
		$action     = $this -> Action     . $this->actionSuffix;

		$cont = $this -> ControllersPath . _DS . $this -> Controller . 
				  $this->controllerSuffix.$this->controllerExtension;

		//fluxMisc::checkSyntaxErrors ($cont); //This one has to be improved

		//You would also have to corroborate the class syntax
		//(sometime after the previous writing)
		//... Which will be done by fluxMisc::checkSyntaxErrors ($code);
		

		if (!class_exists ($controller)) {

			$msg =	'The given controller does not exists in the controllers directory:' . 
						$this -> Controller;

			throw (new fluxXception ($msg));

		}

		$this->Instance = new $controller();

		if (isset ($this->Instance->_useView))
			$this->useView = $this->Instance->_useView; 

		//Once that we have an instance of the given Controller
		//we proceed to verify if the method (Action) exists in the controller class

		$default = $this->actionExists();

		if(!is_null($default))
				  $action = $default;

		unset ($default);

		//Bind every superglobal to the object
		//superglobals should be now accesible as 
		//$this->_server $this->_post $this->_get and so on
		//Inside the required controller

		$this->bindSuperGlobals (); 

		//Bind the document root to the instance, 
		//this is often necessary to know where are we at

		$this->Instance->_documentRoot = '/' . $this->documentRoot;
		$this->Instance->_action       = $this->Action;
		$this->Instance->_controller   = $this->Controller;


		$this->Instance->_fxview = $this->getViewInstance();

		//Here we check if theres an initialization method for the controller

		if ($this->hasStartingMethod()) //If it does, execute that method before anything else
				  $this->Instance->start();

		//If the action has an error handler to be called then call it!
		//$this->hasErrorHandler is a variable that is set to true if an error ocurs
		//i.e: Action not found, or whatever
	
		if ($this->hasErrorHandler){
				  $eHandler = $this->getControllerErrorHandlerAction();
				  $this->Instance->$eHandler();
				  return;
		}

		if (!$this->override)	//This is not an override action it means that it does has a view

				  $this->Instance->$action();

		else {	//This is an override action, which lacks of a view

				  $override = $this->Action.'Override';
				  $this->Instance->$override();

		}

		//If strict mode was set then trigger an exception for every minimal error

		if ($this->strict){	
			if (is_array($err=error_get_last()))
				throw (new fluxXception ($err['message']));
		}

	}

	public function setStrictMode ($bool=FALSE){

			  $this->strict = $bool;

	}

	public function getStrictMode () {
			  $msg  =  'Strict mode is ';
			  $msg .= ($this->strict) ? 'ON' : 'OFF';
			  echo $msg;
	}


	//fluxController - bindSuperglobals : Documented 
	
	private function bindSuperGlobals () {

		///////////////////////////////////////////////
		//Get the parsed Parameters by fluxUri as $_GET
	  
	  if (sizeof($this->Parameters)>0) {
			$getObj = fluxObject::arrayToObject ($this -> Parameters);
			$this -> Instance -> _get = $getObj;
		} else {
			$this -> Instance -> _get = NULL;
		}

		///////////////////////////////////////////////
		//Post parameters will need some filtering if required by the coder

		if (isset($_POST)) {
			$postObj = fluxObject::arrayToObject ($_POST,'');
			$this -> Instance -> _post = $postObj;
		} else {
				  $this -> Instance -> _post = NULL;
		}


		///////////////////////////////////////////////
		//Session variables are also included into the object

		if (isset ($_SESSION)){
			$sessObj =fluxObject::arrayToObject ($_SESSION,''); 
			$this -> Instance -> _session = $sessObj;
		} else {
			$this -> Instance -> _session = NULL;
		}

		///////////////////////////////////////////////
		//Bind the $_SERVER super global  aswell

		if (isset ($_SERVER)){         //Just a way of being pragmatic and keeping it consistent
			$serverObj = fluxObject::arrayToObject ($_SERVER,'');
			$this -> Instance -> _server  = $serverObj;
		} else {
			$this -> Instance -> _server  = NULL;
		}

		///////////////////////////////////////////////
		//Bind the $_COOKIE super global too

		if (isset ($_COOKIE)){
			$cookieObj = fluxObject::arrayToObject ($_COOKIE,'');
			$this -> Instance -> _cookie  = $cookieObj;
		} else {
			$this -> Instance -> _cookie  = NULL;
		}

		//Theres no need to use SG's with the framework
		//The rule of thumb is to get all input sanitized
		//by default, (which could be overriden) if needed 
		//by some reason.

		if ($this->superGlobals=='OFF')
			$this->unsetSuperGlobals();

		return($this->Instance);

	}

	private function unsetSuperGlobals () {

		/* if (isset($_SERVER))	//Other classes being instantiated from within the controller
		 * Cannot be found if we unset $_SERVER
			unset ($_SERVER);			*/

		if(isset($_GET))	
			unset ($_GET);			

		if(isset($_POST))
			unset ($_POST);

		if(isset($_SESSION))
			unset ($_SESSION);

		if(isset($_COOKIE))
			unset ($_COOKIE);

		if(isset($_GLOBALS))
			unset ($_GLOBALS);

	}


	private function setControllerAction () {

		if (is_null ($this -> uriDelimiter))	
			$this -> setUriDelimiter ();

		$this -> parseUri ();

		//$this -> parsedURI is set by $this -> parseURI

		//set the controller and the action

		$this -> Controller = strtolower($this -> parsedURI['controller']);
		$this -> Action     = strtolower($this -> parsedURI['action']);
		$this -> Parameters = $this -> parsedURI['parameters'];

	}


	private function getAction () {

			return (strtolower($this -> Action) . $this->actionSuffix);

	}


	public function setControllersPath ($path=NULL) {

		if (!is_null ($this -> ControllersPath) || !empty ($this -> ControllersPath))
			return ($this -> ControllersPath);

		if (is_null ($path) || empty ($path))
			return ($this -> ControllersPath = 'application' . _DS . 'controllers');

		return ($this -> ControllersPath = $path);

	}

	private function getController () {
			
			return (strtolower($this -> Controller) . 'Controller');

	}


	private function getControllerFile () {
			
			return (strtolower($this -> Controller) . $this->controllerSuffix.$this->controllerExtension);

	}


	public function setStructure ($array=array()) {


		$keys = array_keys ($array);
		

	}

	//Podria devolver el total de clases ... y varias estadisticas relativas 
	//a este controlador o la documentacion ??? :P

	public function __toString () {

		$return = '<h2>' . __CLASS__ . '</h2>';
		$return .= $this -> parsedURI;

		return ($return);

	}

    
}


?>
